package com.auth.utils;

import com.auth.constants.RedisConstants;
import com.auth.dto.Payload;
import com.auth.dto.UserDetails;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.leyou.common.exception.LyException;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;

import javax.crypto.SecretKey;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

//- createJwt() ：生成JWT
//- parseJwt() ：验证并解析JWT

public class JwtUtils {

    //错误：
//    @Autowired
//    RedisTemplate redisTemplate;
    @Autowired
    private StringRedisTemplate redisTemplate;
    /**
     * JWT解析器
     */
    private final JwtParser jwtParser;
    /**
     * 秘钥
     */
    private final SecretKey secretKey;

    private final static ObjectMapper mapper = new ObjectMapper();

    public JwtUtils(String key) {
        // 生成秘钥
        secretKey = Keys.hmacShaKeyFor(key.getBytes(Charset.forName("UTF-8")));
        // JWT解析器
        this.jwtParser = Jwts.parserBuilder().setSigningKey(secretKey).build();
    }

    /**
     * 生成jwt，用默认的JTI
     *
     * @param userDetails 用户信息
     * @return JWT
     */
    public String createJwt(UserDetails userDetails) {
        return createJwt(userDetails, 1800);
    }

    /**
     * 生成jwt，自己指定的过期时间
     *
     * @param userDetails 用户信息
     * @return JWT
     */
    public String createJwt(UserDetails userDetails, int expireSeconds) {
        try {
            //生成jti
            String jti = createJti();
            // 生成token
            String token = Jwts.builder().signWith(secretKey)
                    .setId(jti)
                    .claim("user", mapper.writeValueAsString(userDetails))
                    .setExpiration(DateTime.now().plusSeconds(expireSeconds).toDate())
                    .compact();
            //记录信息在redis中，设置用户到期时间
            System.out.println("userDetails.getId()"+userDetails.getId()+"  jti:"+jti+"  expireSeconds:"+expireSeconds);
            redisTemplate.opsForValue().set(RedisConstants.JTI_KEY_PREFIX + userDetails.getId(), jti,
                    expireSeconds, TimeUnit.SECONDS);
            return token;
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);

        }
    }

    /**
     * 验证并解析jwt，返回包含用户信息的载荷
     *
     * @param jwt token
     * @return 载荷，包含JTI和用户信息
     */
    public Payload parseJwt(String jwt) {
        UserDetails userDetails = null;
        try {
            // 1.验证并解析jwt
            Jws<Claims> claimsJws = jwtParser.parseClaimsJws(jwt);
            Claims claims = claimsJws.getBody();
            // 2.解析载荷数据
            Payload payload = new Payload();
            payload.setJti(claims.getId());
            userDetails = mapper.readValue(claims.get("user", String.class), UserDetails.class);
            payload.setUserDetail(userDetails);

            //验证用户是否过期
            //1.获取redis中的jti
            String rediseJti = redisTemplate.opsForValue().get(RedisConstants.JTI_KEY_PREFIX + userDetails.getId());
            System.out.println("获取到的redis中的jti---》rediseJti = " + rediseJti);
            //2.获取jwt中的jti
            String jti = payload.getJti();
            System.out.println("用户传过来的jti = " + jti);
            //3.比较
            if (rediseJti == null) {
                throw new RuntimeException("登录已经过期!");
            }
            if (rediseJti.equals(jti) == false) {
                // 有jti，但是不是当前jwt，说明其它人登录把当前jwt踢下去了
                throw new RuntimeException("用户可能在其它设备登录!");
            }

            return payload;
        } catch (IllegalArgumentException e) {
            throw new LyException(401, "用户未登录！");
        } catch (JwtException e) {
            throw new LyException(401, "登录无效或者已经超时！");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private String createJti() {
        return StringUtils.replace(UUID.randomUUID().toString(), "-", "");
    }
    /**
     * 刷新jwt有效期
     * @param userId 用户id
     */
    public void refreshJwt(Long userId){
        //使用默认时间
        refreshJwt(userId, RedisConstants.TOKEN_EXPIRE_SECONDS);
    }
    /**
     * 刷新jwt有效期
     * @param userId 用户id
     * @param expireSeconds 有效期
     */
    public void refreshJwt(Long userId, int expireSeconds){
        redisTemplate.expire(RedisConstants.JTI_KEY_PREFIX + userId, expireSeconds, TimeUnit.SECONDS);
    }

}